﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Audio;
using System.IO;

namespace The_LarX
{
    public class WaveReader : Component
    {
        public bool Repeat { get; set; }
        public string WaveFileName { get; set; }
        public string SoundEffectName { get; set; }
        public WaveReader(bool repeat, string filename, string file, GameScreen gs)
            : base(gs)
        {
            Repeat = repeat;
            WaveFileName = filename;
            SoundEffectName = file;
            Process(filename, file);
        }
        public WaveReader()
        {
        }

        private FileStream mFileStream; // For reading the audio file
        /// <summary>
        /// Gets the file size (in bytes).  This is read from a field in the WAV file header.
        /// </summary>
        public long FileSizeBytes
        {
            //get { return mFileSizeBytes; }
            get { return mFileStream.Length; }
        }

        /// <summary>
        /// Gets the number of audio samples in the WAV file.  This is calculated based on
        /// the data size read from the file and the number of bits per sample.
        /// </summary>
        public int NumSamples
        {
            get { return (mDataSizeBytes / (int)(mBitsPerSample / 8)); }
        }

        /// <summary>
        /// Gets the WAV file header as read from the file (array of 4 chars)
        /// </summary>
        public char[] WAVHeader
        {
            get { return mWAVHeader; }
        }
        private char[] mWAVHeader;      // The WAV header (4 bytes, "RIFF")

        private char[] mRIFFType;       // The RIFF type (4 bytes, "WAVE")

        // Audio format information
        private byte mNumChannels;      // The # of channels (1 or 2)
        private int mSampleRateHz;      // The audio sample rate (Hz)
        private int mBytesPerSec;       // Bytes per second
        private short mBytesPerSample;  // # of bytes per sample (1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo)
        private short mBitsPerSample;   // # of bits per sample
        private int mDataSizeBytes;     // The data size (bytes)
        private int mNumSamplesRemaining; // When in read mode, this is used for keeping track of how many audio

        private int mSamplesPerSecond;
        private int mSamplesPerFrame;
        // samples remain.  This is updated in GetNextSample_ByteArray().

        public SoundEffect Sound;
        public void Process(string filename, string sound)
        {
            Sound = Engine.Content.Load<SoundEffect>(sound);

            mFileStream = null;
            mWAVHeader = new char[4];
            mDataSizeBytes = 0;
            mBytesPerSample = 0;
            //mFileSizeBytes = 0;
            mRIFFType = new char[4];

            // These audio format defaults correspond to the standard for
            // CD audio.
            mNumChannels = 2;
            mSampleRateHz = 44100;
            mBytesPerSec = 176400;
            mBitsPerSample = 16;

            try
            {
                mFileStream = File.Open(filename, FileMode.Open);

                // RIFF chunk (12 bytes total)
                // Read the header (first 4 bytes)
                byte[] buffer = new byte[4];
                mFileStream.Read(buffer, 0, 4);
                buffer.CopyTo(mWAVHeader, 0);
                // Read the file size (4 bytes)
                mFileStream.Read(buffer, 0, 4);
                //mFileSizeBytes = BitConverter.ToInt32(buffer, 0);
                // Read the RIFF type
                mFileStream.Read(buffer, 0, 4);
                buffer.CopyTo(mRIFFType, 0);

                // Format chunk (24 bytes total)
                // "fmt " (ASCII characters)
                mFileStream.Read(buffer, 0, 4);
                // Length of format chunk (always 16)
                mFileStream.Read(buffer, 0, 4);
                // 2 bytes (value always 1)
                mFileStream.Read(buffer, 0, 2);
                // # of channels (2 bytes)
                mFileStream.Read(buffer, 2, 2);
                mNumChannels = (BitConverter.IsLittleEndian ? buffer[2] : buffer[3]);
                // Sample rate (4 bytes)
                mFileStream.Read(buffer, 0, 4);
                if (!BitConverter.IsLittleEndian)
                    Array.Reverse(buffer);
                mSampleRateHz = BitConverter.ToInt32(buffer, 0);
                // Bytes per second (4 bytes)
                mFileStream.Read(buffer, 0, 4);
                if (!BitConverter.IsLittleEndian)
                    Array.Reverse(buffer);
                mBytesPerSec = BitConverter.ToInt32(buffer, 0);
                // Bytes per sample (2 bytes)
                mFileStream.Read(buffer, 2, 2);
                if (!BitConverter.IsLittleEndian)
                    Array.Reverse(buffer, 2, 2);
                mBytesPerSample = BitConverter.ToInt16(buffer, 2);
                // Bits per sample (2 bytes)
                mFileStream.Read(buffer, 2, 2);
                if (!BitConverter.IsLittleEndian)
                    Array.Reverse(buffer, 2, 2);
                mBitsPerSample = BitConverter.ToInt16(buffer, 2);

                // Data chunk
                // "data" (ASCII characters)
                mFileStream.Read(buffer, 0, 4);
                // Length of data to follow (4 bytes)
                mFileStream.Read(buffer, 0, 4);
                if (!BitConverter.IsLittleEndian)
                    Array.Reverse(buffer);
                mDataSizeBytes = BitConverter.ToInt32(buffer, 0);

                // Total of 44 bytes read up to this point.

                // The data size should be file size - 36 bytes.  If not, then set
                // it to that.
                if (mDataSizeBytes != FileSizeBytes - 36)
                    mDataSizeBytes = (int)(FileSizeBytes - 36);

                // The rest of the file is the audio data, which
                // can be read by successive calls to NextSample().
                mNumSamplesRemaining = NumSamples;

                mSamplesPerSecond = mBytesPerSec / mBytesPerSample;
            }
            catch
            {
            }
        }

        private bool played;
        public int CurrentSample;
        public override void Update()
        {
            base.Update();

            if (Engine.FPS < 100)
            {
                if (mFileStream.Position < mFileStream.Length)
                {
                    if (!played)
                    {
                        Sound.Play();
                        played = true;
                    }
                    mSamplesPerFrame = mSamplesPerSecond / (int)Engine.FPS;

                    List<int> advance = new List<int>();
                    for (int i = 0; i < mSamplesPerFrame; i++)
                        advance.Add(GetNextSample(mSamplesPerFrame));
                    //int finalk = k / 10000;
                    CurrentSample = IntUtil.Average(advance);
                }
                else
                {
                    if (Repeat)
                    {
                        mFileStream.Position = 0;
                        if (!Engine.Editor)
                        {
                            Sound.Play();
                        }
                    }
                }
            }
        }
        private int GetNextSample(int samplesSkip)
        {
            byte[] audioSample = null;
            try
            {
                int numBytes = mBytesPerSample; // 8 bits per byte
                audioSample = new byte[numBytes];
                mFileStream.Read(audioSample, 0, numBytes);
                //mFileStream.Position += (samplesSkip - 1) * numBytes;
                //--mNumSamplesRemaining;
                return BitConverter.ToInt16(audioSample, 0);
            }
            catch (Exception exc)
            {
                audioSample = null;
            }
            return 0;
        }

        public override void SerializeData()
        {
            base.SerializeData();

            Data.AddData("WaveReader.Repeat", Repeat);
            Data.AddData("WaveReader.WaveFileName", WaveFileName);
            Data.AddData("WaveReader.SoundEffectName", SoundEffectName);
        }

        public override void DeserializeData()
        {
            base.DeserializeData();

            this.Repeat = Data.GetData<bool>("WaveReader.Repeat");
            this.WaveFileName = Data.GetData<string>("WaveReader.WaveFileName");
            this.SoundEffectName = Data.GetData<string>("WaveReader.SoundEffectName");
        }
    }
}
